package fr.cephb.operonj.tools.cmdline.remapper;

import com.sleepycat.bind.tuple.StringBinding;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.Transaction;

import fr.cephb.operonj.core.je.DefaultDuplicateDB;

public class BlatHit
	{
	public static class CandidatePair
	implements Comparable<CandidatePair>
		{
		BlatHit L;
		BlatHit R;
		
		CandidatePair(BlatHit L,BlatHit R)
			{
			this.R=R;
			this.L=L;
			}
		
		public int getChromId()
			{
			assert(R.t_chromId==L.t_chromId);
			return this.R.t_chromId;
			}
		
		public byte getStrand()
			{
			assert(R.strand==L.strand);
			return this.R.strand;
			}
		
		public int getSNPStart()
			{
			return getStrand()=='+'
				?L.t_end
				:R.t_end
				;
				
			}
		
		public int getSNPEnd()
			{
			return getStrand()=='+'
				?R.t_start
				:L.t_start
				;
				
			}
		
		
		public int countMismatches()
			{
			return L.mismatch+R.mismatch;
			}
		
		
		
		
		@Override
		public boolean equals(Object o)
			{
			if(o==this) return true;
			if(o==null || !(o instanceof CandidatePair)) return false;
			CandidatePair cp=CandidatePair.class.cast(o);
			return R.equals(cp.R) && L.equals(cp.L);
			}
		
		@Override
		public int hashCode()
			{
			return R.hashCode()+31*L.hashCode();
			}
		
		@Override
		public int compareTo(CandidatePair o)
			{
			int i= countMismatches()-o.countMismatches();
			if(i!=0) return i;
			i= (L.match+R.match)-(o.L.match+o.R.match);
			if(i!=0) return i*-1;//negate
			return 0;
			}
		@Override
		public String toString()
			{
			return " L :"+L+"\n R :"+R+"\n SNP: ("+getChromId()+")"+getSNPStart()+"-"+getSNPEnd();
			}
		
	
		
		}
	
	
	static public class BlatBinding extends
	TupleBinding<BlatHit>
		{

		@Override
		public BlatHit entryToObject(TupleInput input)
			{
			BlatHit h=new BlatHit();
			h.match= input.readInt();//  	number of matching nucleotides in alignment that aren't part of repeats
			h.mismatch=input.readInt();// 	number of nucleotides in alignment that don't match
			h.rep_match=input.readInt();// 	number of nucleotides in alignment that are part of repeats
			h.N_s=input.readInt();// 	number of N's in alignment
			h.q_gap_count=input.readInt();// 	number of inserts in query sequence
			h.q_gap_bases=input.readInt();// 	number of nucleotides inserted in query sequence
			h.t_gap_count=input.readInt();// 	number of inserts in target sequence (chromosome)
			h.t_gap_bases=input.readInt();// 	number of nucleotides inserted in target sequence (chromosome)
			h.strand=input.readByte();// 	chromsome strand (+ or -)
			h.q_name=input.readString();// 	name of query sequence
			h.q_size=input.readInt();// 	length of query sequence
			h.q_start=input.readInt();// 	start of query sequence in alignment
			h.q_end=input.readInt(); //end of query sequence in alignment
			h.t_chromId=input.readInt();// 	matching chromsome ("target", ex: chr13)
			h.t_size=input.readInt();// 	length of target sequence (chromosome)
			h.t_start=input.readInt();// 	start of target sequence (chromosome) comprising alignment
			h.t_end=input.readInt();// 	end of target sequence (chromosome) comprising alignment
			h.block_count=input.readInt();// 	number of blocks (may be exons for cDNA) of matching regions
			h.blockSizes=input.readString();// 	sizes of blocks (may be exons for cDNA) of matching regions (delimited by commas)
			h.qStarts=input.readString();// 	list of query nts at starts of blocks (delimited by commas)
			h.tStarts=input.readString();// 	list of chromsome nts at starts of blocks (delim
			return h;
			}

		@Override
		public void objectToEntry(BlatHit object, TupleOutput output)
			{
			output.writeInt(object.match);//  	number of matching nucleotides in alignment that aren't part of repeats
			output.writeInt(object.mismatch);// 	number of nucleotides in alignment that don't match
			output.writeInt(object.rep_match);// 	number of nucleotides in alignment that are part of repeats
			output.writeInt(object.N_s);// 	number of N's in alignment
			output.writeInt(object.q_gap_count);// 	number of inserts in query sequence
			output.writeInt(object.q_gap_bases);// 	number of nucleotides inserted in query sequence
			output.writeInt(object.t_gap_count);// 	number of inserts in target sequence (chromosome)
			output.writeInt(object.t_gap_bases);// 	number of nucleotides inserted in target sequence (chromosome)
			output.writeByte(object.strand);// 	chromsome strand (+ or -)
			output.writeString(object.q_name);// 	name of query sequence
			output.writeInt(object.q_size);// 	length of query sequence
			output.writeInt(object.q_start);// 	start of query sequence in alignment
			output.writeInt(object.q_end); //end of query sequence in alignment
			output.writeInt(object.t_chromId);// 	matching chromsome ("target", ex: chr13)
			output.writeInt(object.t_size);// 	length of target sequence (chromosome)
			output.writeInt(object.t_start);// 	start of target sequence (chromosome) comprising alignment
			output.writeInt(object.t_end);// 	end of target sequence (chromosome) comprising alignment
			output.writeInt(object.block_count);// 	number of blocks (may be exons for cDNA) of matching regions
			output.writeString(object.blockSizes);// 	sizes of blocks (may be exons for cDNA) of matching regions (delimited by commas)
			output.writeString(object.qStarts);// 	list of query nts at starts of blocks (delimited by commas)
			output.writeString(object.tStarts);// 	list of chromsome nts at starts of blocks (delimited by commas)
			}
		
		}
	
	static public class HitDB extends
		DefaultDuplicateDB<String, BlatHit>
		{

		public HitDB(Environment env, Transaction txn,
				DatabaseConfig dbConfig) throws DatabaseException
			{
			super(env, txn, "blatHits", dbConfig,
					new StringBinding(),
					new BlatBinding());
			}
		
		}
	int match;//  0	number of matching nucleotides in alignment that aren't part of repeats
	int mismatch;//1 	number of nucleotides in alignment that don't match
	int rep_match;//2 	number of nucleotides in alignment that are part of repeats
	int N_s;//3 	number of N's in alignment
	int q_gap_count;// 4	number of inserts in query sequence
	int q_gap_bases;//5 	number of nucleotides inserted in query sequence
	int t_gap_count;//6 	number of inserts in target sequence (chromosome)
	int t_gap_bases;//7 	number of nucleotides inserted in target sequence (chromosome)
	byte strand;//8 	chromsome strand (+ or -)
	String q_name;//9 	name of query sequence
	int q_size;//10 	length of query sequence
	int q_start;//11 	start of query sequence in alignment
	int q_end; //12 end of query sequence in alignment
	int t_chromId;//13 	matching chromsome ("target", ex: chr13)
	int t_size;//14 	length of target sequence (chromosome)
	int t_start;// 	start of target sequence (chromosome) comprising alignment
	int t_end;// 	end of target sequence (chromosome) comprising alignment
	int block_count;// 	number of blocks (may be exons for cDNA) of matching regions
	String blockSizes;// 	sizes of blocks (may be exons for cDNA) of matching regions (delimited by commas)
	String qStarts;// 	list of query nts at starts of blocks (delimited by commas)
	String tStarts;// 	list of chromsome nts at starts of blocks (delimited by commas)
	//qSeqs* 	list of query sequence blocks (delimited by commas)
	BlatHit()
		{
		}
	
	BlatHit(String token[])
		{
		int index=0;
		this.match=Integer.parseInt(token[index++]);//  	number of matching nucleotides in alignment that aren't part of repeats
		this.mismatch=Integer.parseInt(token[index++]);// 	number of nucleotides in alignment that don't match
		this.rep_match=Integer.parseInt(token[index++]);// 	number of nucleotides in alignment that are part of repeats
		this.N_s=Integer.parseInt(token[index++]);// 	number of N's in alignment
		this.q_gap_count=Integer.parseInt(token[index++]);// 	number of inserts in query sequence
		this.q_gap_bases=Integer.parseInt(token[index++]);// 	number of nucleotides inserted in query sequence
		this.t_gap_count=Integer.parseInt(token[index++]);// 	number of inserts in target sequence (chromosome)
		this.t_gap_bases=Integer.parseInt(token[index++]);// 	number of nucleotides inserted in target sequence (chromosome)
		this.strand=(byte)token[index++].charAt(0);// 	chromsome strand (+ or -)
		this.q_name=token[index++];// 	name of query sequence
		this.q_size=Integer.parseInt(token[index++]);// 	length of query sequence
		this.q_start=Integer.parseInt(token[index++]);// 	start of query sequence in alignment
		this.q_end=Integer.parseInt(token[index++]); //end of query sequence in alignment
		this.t_chromId=Integer.parseInt(token[index++]);// 	matching chromsome ("target", ex: chr13)
		this.t_size=Integer.parseInt(token[index++]);// 	length of target sequence (chromosome)
		this.t_start=Integer.parseInt(token[index++]);// 	start of target sequence (chromosome) comprising alignment
		this.t_end=Integer.parseInt(token[index++]);// 	end of target sequence (chromosome) comprising alignment
		this.block_count=Integer.parseInt(token[index++]);// 	number of blocks (may be exons for cDNA) of matching regions
		this.blockSizes=token[index++];// 	sizes of blocks (may be exons for cDNA) of matching regions (delimited by commas)
		this.qStarts=token[index++];// 	list of query nts at starts of blocks (delimited by commas)
		this.tStarts=token[index++];// 	list of chromsome nts at starts of blocks (delimited by commas)
		}
	@Override
	public int hashCode()
		{
		final int prime = 31;
		int result = 1;
		result = prime * result + t_chromId;
		result = prime * result + t_start;
		result = prime * result + t_end;
		result = prime * result + strand;
		result = prime * result + q_name.hashCode();
		return result;
		}



	@Override
	public boolean equals(Object obj)
		{
		if (this == obj) return true;
		if (obj == null) return false;
		if (!(obj instanceof BlatHit)) return false;
		BlatHit other = (BlatHit) obj;
		
		if (t_chromId != other.t_chromId) return false;
		if (t_start != other.t_start) return false;
		if (t_end != other.t_end) return false;
		if (strand != other.strand) return false;
		if( !q_name.equals(other.q_name)) return false;
		return true;
		}



	@Override
    public String toString()
		{
        return "(chr"+t_chromId+")"+t_start+"-"+t_end+"["+(char)strand+"]"+
        	" q_size:"+this.q_size+
        	(this.mismatch==0?"":" mismatches:"+this.mismatch+" ")+
        	(this.block_count==0?"":" block_count:"+this.block_count+" ")+
        	(this.rep_match==0?"":" rep_match:"+this.rep_match+" ")+
        	(this.q_gap_bases==0?"":" q_gap_bases:"+this.q_gap_bases+" ")+
        	(this.q_gap_count==0?"":" q_gap_count:"+this.q_gap_count+" ")+
        	(this.t_gap_bases==0?"":" t_gap_bases:"+this.t_gap_bases+" ")+
        	(this.t_gap_count==0?"":" t_gap_count:"+this.t_gap_count+" ")
        	;
        }
	}
